<?php
/**
* file upload class. <br />
* 
* @author liuxiao <seanliu707@gmial.com>
* @copyright Swolf 2015
* @version 1.0
*/
define('TIMER',1<<0);
define('UNIQID',1<<1);

class Upload
{
	private $_accept_exts;
	private $_refuse_exts;
	private $_max_size;
	private $_upload_file_path = null;		//the file path when upload one file.
	private static $_instance = null;
	private $_error = NULL;
	
	public static function getInstantce( $max_file_size = 0 , $allow_exts = null , $refuse_exts = null ){
		if ( self::$_instance == null )
			self::$_instance = new self($max_file_size , $allow_exts, $refuse_exts);
		return self::$_instance;
	}
	
	private function __construct($max_file_size, $allow_exts, $refuse_exts){
		$this->_max_size 	= $max_file_size;
		$this->_accept_exts = $allow_exts;
		$this->_refuse_exts = $refuse_exts;
		
		if ( $max_file_size > ini_get('upload_max_filesize') ) ini_set('upload_max_filesize', $max_file_size);
	}
	/**
	 * Upload one file.
	 * @param string $name
	 * @param string $upload_dir
	 * @param mixed $name_rule
	 * @param bool $overwrite
	 */
	public function uploadFile($name,$upload_dir,$name_rule=null,$overwrite=true) {
		
		$upload_path = $this->checkUploadDir($upload_dir);
		$_local	= $_FILES[$name]['name'];
		$_temps	= $_FILES[$name]['tmp_name'];
		$_sizes	= $_FILES[$name]['size'];
		
		$_name_arr = '';
		$_path_arr = '';
		
		$_n = count($_local);
		
		if ( $_n == 1 ) {
			if ( $this->checkFileExt($_local) == false ) {
				$this->_error = 'File extension error.';
				return FALSE;
			}
			if ( $_sizes < 0 || $_sizes > $this->_max_size ) {
				$this->_error = 'File size error.';
				return FALSE;
			}
		
			$file_name = $this->setFileName($_local,$name_rule);
			$file_path = $upload_path.$file_name;
		
			if ( $overwrite || ! file_exists($file_path) )
				if ( move_uploaded_file($_temps, $file_path) ) {
				$_name_arr = $file_name;
				$_path_arr = $file_path;
			}
		} elseif ( $_n > 1 ) {
			for ( $i = 0 ; $i < $_n ; $i++ ) {
				if ( $this->checkFileExt($_local[$i]) == false ) {
					$this->_error = 'File extension error: '.$i;
					return FALSE;
				}
				if ( $_sizes[$i] < 0 || $_sizes[$i] > $this->_max_size ) {
					$this->_error = 'File size error: '.$i;
					return FALSE;
				}
				
				$file_name = $this->setFileName($_local[$i],$name_rule);
				$file_path = $upload_path.$file_name;
				
				if ( $overwrite || ! file_exists($file_path) )
					if ( move_uploaded_file($_temps[$i], $file_path) ) {
						$_name_arr[$i] = $file_name;
						$_path_arr[$i] = $file_path;
					}
			}
		}
		
		$this->_upload_file_path = $_path_arr;
		return $_name_arr;
	}

	/**
	 * return the uploaded file path or path array.
	 * @return mixed
	 */
	public function getUploadFilePath(){
		if ( $this->_upload_file_path != null ) return $this->_upload_file_path;
		return FALSE;	
	}
	
	/**
	 * return the error infomation.
	 * @return String.
	 */
	public function getError () {
		return $this->_error;
	}
	
	/**
	 * Download file.
	 * @param string $file_path
	 * @return string
	 */
	public function downloadFile($file_path){
		if( ! file_exists($file_path) ) return FALSE;
		$file = fopen($file_path, 'r');
		$file_size = filesize( $file_path );
		header('Content-type: application/octet-stream');
		header('Accept-Ranges: bytes');
		header('Accept-Length: '.$file_size);
		header('Content-Disposition: attachment; filename=' . basename($file_path) );
		header('Pragma: no-cache');
		header('Cache-Control: No-cache');
		header('Expires: 0');

		echo  @fread($file,$file_size);
		fclose($file);
		exit;
	}
	/**
	 * Check and create the uload directory.
	 * @param string $dir
	 * @return mixed
	 */
	private function checkUploadDir( $dir ) {
		$dir = preg_replace('/[\\\\\/]+/i', '/', trim($dir));
		if ( $dir != '' ){
			if ( $dir == './' ) return './';
		} elseif (  $dir === '0' )	return './0/';
		else return './';

		if ( ! file_exists($dir) ){
			$dir_arr = explode('/', $dir);
			$path = '';
			foreach ( $dir_arr as $value ){
				$path .= $value.'/';
				if ( ! file_exists($path) ) @mkdir($path,0777) ;
			}
		}
		if ( substr($dir,-1)!='/' ) $dir .= '/';
		return $dir;
	}
	/**
	 * Get the file`s extension.
	 * @param string $file_name
	 * @param int $point
	 */
	private function getExtension( $file_name , $with_point = 0){
	    $arr = explode('.', $file_name);
		$ext = array_pop($arr);
		if ( $with_point == 0 )	return $ext;
		if ( $with_point == 1 )	return '.'.$ext;
		exit('Warning: The second argument must be 0 or 1 !');
	}
	/**
	 * Check the file extention is legitimate.
	 * @param string $file_name	
	 * @return bool
	 */
	private function checkFileExt($file_name){
		$file_ext = $this->getExtension($file_name);
		$accept_ext_arr = explode('|', $this->_accept_exts);
		$refuse_ext_arr = explode('|', $this->_refuse_exts);
		
		if ( ! in_array($file_ext, $accept_ext_arr) )	return FALSE;
		if ( in_array($file_ext, $refuse_ext_arr) )		return FALSE;
		return true;
	}
	/**
	 * Set the upload file name.
	 * @param string $form_file
	 * @param constant $rule / TIMER or UNIQID
	 * @return string
	 */
	private function setFileName($file_name, $rule){
		if ( ! ini_get('date.timezone') ) ini_set('date.timezone', 'PRC');
		if ( $rule == TIMER ){
			list($usec) = explode(' ', microtime());
			$usec = str_replace('0.', '', $usec);
			$new_name = date('YmdHis').$usec.$this->getExtension($file_name,1);
		} elseif ( $rule == UNIQID ){
			$new_name = md5(uniqid()).$this->getExtension($file_name,1);
		}else{
			$new_name = $file_name;
		}
		return $new_name;
	}
}